// This file is distributed under a BSD license. See LICENSE.txt for details.

asc
{
  cbuffer CubeHordeBasePara : register(c0) : slot vs 0
  {
    float4x4 mvp;
    float4 lightDir;
    float4x3 lightMvp;
    float3 up;
    float4 shadowCol;
    
    float4 positions[8];
    float4 normals[6];
  };
  
  cbuffer CubeHordePixelPara : register(c0) : slot ps 0
  {
    float4 darkCol;
    float4 lightCol;
    float4 clipPlane;
  };
  
  cbuffer CubeHordeXForm : register(c32) : slot vs 1
  {
    float4 transform[224]; // 112*2
  };
  
  cbuffer BlurPara : register(c0) : slot vs 0
  {
    float4x4 mvp;
    float2 pixelOffset;
  };
}

// TODO: check out how to use functions with ASC so I don't have to copy&paste
// the cube stuff every time :)

// (variance) shadow map write
shader CubeHordeShadowWrite : sASCMaterial
{
  vs
  {
    asc vs_2_0
    {
      use CubeHordeBasePara;
      use CubeHordeXForm;
    
      void main(
        in int4 indices : POSITION,
        in float4 in_col : COLOR,
        out float4 out_pos : POSITION,
        out float out_z : TEXCOORD0,
      )
      {
        // get input params
        float4 in_pos = positions[indices.x];

        // x axis
        float3 x_axis = transform[indices.z];
        
        // scale
        float lenXSq = dot(x_axis,x_axis);
        float lenXRSq = rsqrt(lenXSq);
        x_axis *= lenXRSq;
        
        float3 scale;
        scale.x = lenXSq * lenXRSq;
        scale.y = transform[indices.z].w;
        scale.z = transform[indices.z+1].w;
        
        // get transform for this cube
        float3 z_axis = normalize(cross(x_axis,up));
        float3 y_axis = cross(z_axis,x_axis);
        float4 xlate = transform[indices.z+1];
        
        // calc worldspace position
        float4 world_pos;
        float3 scaled_pos;
        
        scaled_pos = in_pos.xyz * scale;
        
        world_pos.xyz = scaled_pos.x * x_axis;
        world_pos.xyz += scaled_pos.y * y_axis;
        world_pos.xyz += scaled_pos.z * z_axis;
        world_pos.xyz += xlate.xyz;
        world_pos.w = 1.0f;
        
        // calc output position+z
        out_pos = mul(world_pos,mvp);
        out_z = mul(world_pos,lightMvp).z;
      }
    }
  }
  
  ps
  {
    asc ps_2_0
    {
      void main(
        in float4 in_col : COLOR,
        in float in_z : TEXCOORD0,
        out float4 result : COLOR
      )
      {
        result = in_z;
      }
    }
  }
};

// render with shadow map
shader CubeHordeShadowRender : sASCMaterial
{
  vs
  {
    asc vs_2_0
    {
      use CubeHordeBasePara;
      use CubeHordeXForm;
    
      void main(
        in int4 indices : POSITION,
        in float4 in_col : COLOR,
        out float4 out_pos : POSITION,
        out float4 out_col : COLOR,
        out float3 out_mpos : TEXCOORD0,
        out float3 out_ldir : TEXCOORD1,
        out float3 out_lrpos : TEXCOORD2, // light-relative position
      )
      {
        // get input params
        float4 in_pos = positions[indices.x];

        // x axis
        float3 x_axis = transform[indices.z];
        
        // scale
        float lenXSq = dot(x_axis,x_axis);
        float lenXRSq = rsqrt(lenXSq);
        x_axis *= lenXRSq;
        
        float3 scale;
        scale.x = lenXSq * lenXRSq;
        scale.y = transform[indices.z].w;
        scale.z = transform[indices.z+1].w;
        
        // get transform for this cube
        float3 z_axis = normalize(cross(x_axis,up));
        float3 y_axis = cross(z_axis,x_axis);
        float4 xlate = transform[indices.z+1];
        
        // calc worldspace position
        float4 world_pos;
        float3 scaled_pos;
        
        scaled_pos = in_pos.xyz * scale;
        
        world_pos.xyz = scaled_pos.x * x_axis;
        world_pos.xyz += scaled_pos.y * y_axis;
        world_pos.xyz += scaled_pos.z * z_axis;
        world_pos.xyz += xlate.xyz;
        world_pos.w = 1.0f;
        
        // calc output position/color/light dir
        out_pos = mul(world_pos,mvp);
        out_col = in_col;
        out_mpos = in_pos;
        out_ldir = lightDir;
        out_ldir.x = dot(lightDir,x_axis);
        out_ldir.y = dot(lightDir,y_axis);
        out_ldir.z = dot(lightDir,z_axis);
        out_lrpos = mul(world_pos,lightMvp);
      }
    }
  }
  
  ps
  {
    asc ps_2_0
    {
      use CubeHordePixelPara;
      
      samplerCUBE sampNormal : register(s0);
      sampler2D sampShadow : register(s1);
    
      void main(
        in float4 in_col : COLOR,
        in float3 in_mpos : TEXCOORD0,
        in float3 in_ldir : TEXCOORD1,
        in float3 in_lrpos : TEXCOORD2,
        out float4 result : COLOR
      )
      {
        // normal from model position
        float3 normal = texCUBE(sampNormal,in_mpos);
        
        // shade (diffuse, directional)
        float lightTerm = saturate(dot(normal,in_ldir));
        float4 baseCol = in_col * saturate(dot(normal,in_ldir));
        
        // read shadow map, variance shadow map computation
        float2 moments = tex2D(sampShadow,in_lrpos.xy);
        float litFactor = in_lrpos.z <= moments.x+0.01;
        result = lerp(darkCol,lightCol,lightTerm * (litFactor * 0.9 + 0.1));
        //result = baseCol * (litFactor * 0.9 + 0.1);
        
        /*float variance = moments.y - moments.x*moments.x;
        float md = moments.x - in_lrpos.z;
        float pMax = min(variance / (variance + md*md),1.0f);
        
        result = baseCol * (max(litFactor,pMax) * 0.9 + 0.1);*/
      }
    }
  }
};

// render with shadow map and plane clip
shader CubeHordeShadowRenderClip : sASCMaterial
{
  vs
  {
    asc vs_2_0
    {
      use CubeHordeBasePara;
      use CubeHordeXForm;
    
      void main(
        in int4 indices : POSITION,
        in float4 in_col : COLOR,
        out float4 out_pos : POSITION,
        out float4 out_col : COLOR,
        out float3 out_mpos : TEXCOORD0,
        out float3 out_ldir : TEXCOORD1,
        out float3 out_lrpos : TEXCOORD2, // light-relative position
        out float3 out_wpos : TEXCOORD3, // world position
      )
      {
        // get input params
        float4 in_pos = positions[indices.x];

        // x axis
        float3 x_axis = transform[indices.z];
        
        // scale
        float lenXSq = dot(x_axis,x_axis);
        float lenXRSq = rsqrt(lenXSq);
        x_axis *= lenXRSq;
        
        float3 scale;
        scale.x = lenXSq * lenXRSq;
        scale.y = transform[indices.z].w;
        scale.z = transform[indices.z+1].w;
        
        // get transform for this cube
        float3 z_axis = normalize(cross(x_axis,up));
        float3 y_axis = cross(z_axis,x_axis);
        float4 xlate = transform[indices.z+1];
        
        // calc worldspace position
        float4 world_pos;
        float3 scaled_pos;
        
        scaled_pos = in_pos.xyz * scale;
        
        world_pos.xyz = scaled_pos.x * x_axis;
        world_pos.xyz += scaled_pos.y * y_axis;
        world_pos.xyz += scaled_pos.z * z_axis;
        world_pos.xyz += xlate.xyz;
        world_pos.w = 1.0f;
        
        // calc output position/color/light dir
        out_pos = mul(world_pos,mvp);
        out_col = in_col;
        out_mpos = in_pos;
        out_ldir = lightDir;
        out_ldir.x = dot(lightDir,x_axis);
        out_ldir.y = dot(lightDir,y_axis);
        out_ldir.z = dot(lightDir,z_axis);
        out_lrpos = mul(world_pos,lightMvp);
        out_wpos = world_pos;
      }
    }
  }
  
  ps
  {
    asc ps_2_0
    {
      use CubeHordePixelPara;
      
      samplerCUBE sampNormal : register(s0);
      sampler2D sampShadow : register(s1);
    
      void main(
        in float4 in_col : COLOR,
        in float3 in_mpos : TEXCOORD0,
        in float3 in_ldir : TEXCOORD1,
        in float3 in_lrpos : TEXCOORD2,
        in float3 in_wpos : TEXCOORD3,
        out float4 result : COLOR
      )
      {
        // normal from model position
        float3 normal = texCUBE(sampNormal,in_mpos);
        
        // shade (diffuse, directional)
        float lightTerm = saturate(dot(normal,in_ldir));
        float4 baseCol = in_col * saturate(dot(normal,in_ldir));
        
        // read shadow map, variance shadow map computation
        float2 moments = tex2D(sampShadow,in_lrpos.xy);
        float litFactor = in_lrpos.z <= moments.x+0.01;
        result = lerp(darkCol,lightCol,lightTerm * (litFactor * 0.9 + 0.1));
        
        // clip this!
        clip(dot(float4(in_wpos,1),clipPlane));
      }
    }
  }
};

// lotsa cubes ahoy!
shader CubeHordeShaderClip : sASCMaterial
{
  vs
  {
    asc vs_2_0
    {
      use CubeHordeBasePara;
      use CubeHordeXForm;
    
      void main(
        in int4 indices : POSITION,
        in float4 in_col : COLOR,
        out float4 out_pos : POSITION,
        out float4 out_col : COLOR,
        out float3 out_mpos : TEXCOORD0,
        out float3 out_ldir : TEXCOORD1,
        out float3 out_wpos : TEXCOORD2,
      )
      {
        // get input params
        float4 in_pos = positions[indices.x];

        // x axis
        float3 x_axis = transform[indices.z];
        
        // scale
        float lenXSq = dot(x_axis,x_axis);
        float lenXRSq = rsqrt(lenXSq);
        x_axis *= lenXRSq;
        
        float3 scale;
        scale.x = lenXSq * lenXRSq;
        scale.y = transform[indices.z].w;
        scale.z = transform[indices.z+1].w;
        
        // get transform for this cube
        float3 z_axis = normalize(cross(x_axis,up));
        float3 y_axis = cross(z_axis,x_axis);
        float4 xlate = transform[indices.z+1];
        
        // calc worldspace position
        float4 world_pos;
        float3 scaled_pos;
        
        scaled_pos = in_pos.xyz * scale;
        
        world_pos.xyz = scaled_pos.x * x_axis;
        world_pos.xyz += scaled_pos.y * y_axis;
        world_pos.xyz += scaled_pos.z * z_axis;
        world_pos.xyz += xlate.xyz;
        world_pos.w = 1.0f;
        
        // calc output position/color/light dir
        out_pos = mul(world_pos,mvp);
        out_col = in_col;
        out_mpos = in_pos;
        out_ldir = lightDir;
        out_ldir.x = dot(lightDir,x_axis);
        out_ldir.y = dot(lightDir,y_axis);
        out_ldir.z = dot(lightDir,z_axis);
        out_wpos = world_pos;
      }
    }
  }
  
  ps
  {
    asc ps_2_0
    {
      use CubeHordePixelPara;
    
      samplerCUBE sampNormal : register(s0);
    
      void main(
        in float4 in_col : COLOR,
        in float3 in_mpos : TEXCOORD0,
        in float3 in_ldir : TEXCOORD1,
        in float3 in_wpos : TEXCOORD2,
        out float4 result : COLOR
      )
      {
        // normal from model position
        float3 normal = texCUBE(sampNormal,in_mpos);
        
        // shade (diffuse, directional)
        float lightTerm = saturate(dot(normal,in_ldir));
        result = lerp(darkCol,lightCol,lightTerm);

        // clip this!
        clip(dot(float4(in_wpos,1),clipPlane));
      }
    }
  }
};

// lotsa cubes ahoy!
shader CubeHordeShader : sASCMaterial
{
  vs
  {
    asc vs_2_0
    {
      use CubeHordeBasePara;
      use CubeHordeXForm;
    
      void main(
        in int4 indices : POSITION,
        in float4 in_col : COLOR,
        out float4 out_pos : POSITION,
        out float4 out_col : COLOR,
        out float3 out_mpos : TEXCOORD0,
        out float3 out_ldir : TEXCOORD1,
      )
      {
        // get input params
        float4 in_pos = positions[indices.x];

        // x axis
        float3 x_axis = transform[indices.z];
        
        // scale
        float lenXSq = dot(x_axis,x_axis);
        float lenXRSq = rsqrt(lenXSq);
        x_axis *= lenXRSq;
        
        float3 scale;
        scale.x = lenXSq * lenXRSq;
        scale.y = transform[indices.z].w;
        scale.z = transform[indices.z+1].w;
        
        // get transform for this cube
        float3 z_axis = normalize(cross(x_axis,up));
        float3 y_axis = cross(z_axis,x_axis);
        float4 xlate = transform[indices.z+1];
        
        // calc worldspace position
        float4 world_pos;
        float3 scaled_pos;
        
        scaled_pos = in_pos.xyz * scale;
        
        world_pos.xyz = scaled_pos.x * x_axis;
        world_pos.xyz += scaled_pos.y * y_axis;
        world_pos.xyz += scaled_pos.z * z_axis;
        world_pos.xyz += xlate.xyz;
        world_pos.w = 1.0f;
        
        // calc output position/color/light dir
        out_pos = mul(world_pos,mvp);
        out_col = in_col;
        out_mpos = in_pos;
        out_ldir = lightDir;
        out_ldir.x = dot(lightDir,x_axis);
        out_ldir.y = dot(lightDir,y_axis);
        out_ldir.z = dot(lightDir,z_axis);
      }
    }
  }
  
  ps
  {
    asc ps_2_0
    {
      use CubeHordePixelPara;
    
      samplerCUBE sampNormal : register(s0);
    
      void main(
        in float4 in_col : COLOR,
        in float3 in_mpos : TEXCOORD0,
        in float3 in_ldir : TEXCOORD1,
        out float4 result : COLOR
      )
      {
        // normal from model position
        float3 normal = texCUBE(sampNormal,in_mpos);
        
        // shade (diffuse, directional)
        float lightTerm = saturate(dot(normal,in_ldir));
        result = lerp(darkCol,lightCol,lightTerm);
      }
    }
  }
};

// for the ground plane; no lighting, no tex, no zwrite variant.
// (this is a normal render-triangles-with-VSM shader)
shader GroundPlaneShader : sASCMaterial
{
  vs
  {
    asc vs_2_0
    {
      use CubeHordeBasePara;
    
      void main(
        in float4 in_pos : POSITION,
        in float4 in_col : COLOR,
        out float4 out_pos : POSITION,
        out float4 out_col : COLOR,
        out float3 out_lrpos : TEXCOORD0, // light-relative position
      )
      {
        // this is pretty straightforward.
        out_pos = mul(in_pos,mvp);
        out_col = shadowCol;
        out_lrpos = mul(in_pos,lightMvp);
      }
    }
  }
  
  ps
  {
    asc ps_2_0
    {
      sampler2D sampShadow : register(s0);
    
      void main(
        in float4 in_col : COLOR,
        in float3 in_lrpos : TEXCOORD0,
        out float4 result : COLOR
      )
      {
        // read shadow map, variance shadow map computation
        float2 moments = tex2D(sampShadow,in_lrpos.xy);
        float litFactor = saturate(in_lrpos.z) <= moments.x+0.01;
        //result = in_col * (litFactor * 0.9 + 0.1);
        result = lerp(in_col, float4(0,0,0,0), litFactor * 0.9 + 0.1);
        
        /*float variance = moments.y - moments.x*moments.x;
        float md = moments.x - in_lrpos.z;
        float pMax = min(variance / (variance + md*md),1.0f);
        result = in_col * (max(litFactor,pMax) * 0.9 + 0.1);*/
      }
    }
  }
};

// simple blur shader
shader BlurShader : sASCMaterial
{
  vs
  {
    asc vs_2_0
    {
      use BlurPara;
    
      void main(
        in float4 in_pos : POSITION,
        in float4 in_uv : TEXCOORD0,
        out float4 out_pos : POSITION,
        out float2 out_uv0 : TEXCOORD0,
        out float2 out_uv1 : TEXCOORD1,
        out float2 out_uv2 : TEXCOORD2,
        out float2 out_uv3 : TEXCOORD3,
        out float2 out_uv4 : TEXCOORD4,
      )
      {
        // this is pretty straightforward.
        out_pos = mul(in_pos,mvp);
        out_uv0 = in_uv - 2 * pixelOffset;
        out_uv1 = in_uv - pixelOffset;
        out_uv2 = in_uv;
        out_uv3 = in_uv + pixelOffset;
        out_uv4 = in_uv + 2 * pixelOffset;
      }
    }
  }
  
  ps
  {
    asc ps_2_0
    {
      sampler2D samp0 : register(s0);
      sampler2D samp1 : register(s1);
      sampler2D samp2 : register(s2);
    
      void main(
        in float2 in_uv0 : TEXCOORD0,
        in float2 in_uv1 : TEXCOORD1,
        in float2 in_uv2 : TEXCOORD2,
        in float2 in_uv3 : TEXCOORD3,
        in float2 in_uv4 : TEXCOORD4,
        out float4 result : COLOR
      )
      {
        // read pixels
        float4 p0 = tex2D(samp0,in_uv0);
        float4 p1 = tex2D(samp1,in_uv1);
        float4 p2 = tex2D(samp2,in_uv2);
        float4 p3 = tex2D(samp0,in_uv3);
        float4 p4 = tex2D(samp1,in_uv4);
        
        // weighted sum for results: (1 4 6 4 1)/16
        result = 1.0/16.0 * (p0 + p4) + 1.0/4.0 * (p1 + p3) + 3.0/8.0 * p2;
      }
    }
  }
};
